package com.zjxf.session;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.zjxf.cache.CacheKeys;
import com.zjxf.common.ShiroConst;
import com.zjxf.redis.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * created with IntelliJ IDEA
 *
 * @author: create by limu
 * Date: 2020/7/20
 * Time：11:58
 */
@Slf4j
@Service
public class RedisSessionDao extends AbstractSessionDAO {

    @Autowired
    private RedisService redisService;

    public RedisSessionDao() {
        super();
    }

    public RedisSessionDao(RedisService redisService) {
        super();
        this.redisService = redisService;
    }

    @Override
    protected Serializable doCreate(Session session) {
        log.debug("会话建立...");
        Serializable sessionId = this.generateSessionId(session);
        this.assignSessionId(session, sessionId);

        JSONObject redisJson = new JSONObject();
        redisJson.put("session", sessionToByte(session));
        redisService.setContainExpire(CacheKeys.getSystemColonyRedisSession(String.valueOf(session.getId())), redisJson, ShiroConst.SHIRO_SESSION_SESSION_MAX_AGE, TimeUnit.SECONDS);
        return sessionId;
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        if (Objects.nonNull(sessionId)) {
            Optional<JSONObject> redisJsonOptional = redisService.get(CacheKeys.getSystemColonyRedisSession(String.valueOf(sessionId)));
            return redisJsonOptional.map(item -> byteToSession(item.getBytes("session"))).orElse(null);
        } else {
            return null;
        }
    }

    @Override
    public void update(Session session) throws UnknownSessionException {
        if (Objects.nonNull(session) && Objects.nonNull(session.getId())) {
            JSONObject redisJson = new JSONObject();
            redisJson.put("session", sessionToByte(session));
            redisService.setContainExpire(CacheKeys.getSystemColonyRedisSession(String.valueOf(session.getId())), redisJson, ShiroConst.SHIRO_SESSION_SESSION_MAX_AGE, TimeUnit.SECONDS);
        }
    }

    @Override
    public void delete(Session session) {
        log.debug("删除会话...");
        if (Objects.nonNull(session) && Objects.nonNull(session.getId())) {
            redisService.delete(CacheKeys.getSystemColonyRedisSession(String.valueOf(session.getId())));
        }
    }

    @Override
    public Collection<Session> getActiveSessions() {
        return redisService.keysValue(CacheKeys.getSystemColonyRedisSession(StringUtils.EMPTY)).stream()
                .map(item -> byteToSession(item.getBytes("session"))).collect(Collectors.toCollection(Lists::newArrayList));
    }

    /**
     * session序列化问题处理
     *
     * @param session 会话
     * @return sessionToByte
     */
    public byte[] sessionToByte(Session session) {
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        byte[] bytes = null;
        try {
            ObjectOutputStream oo = new ObjectOutputStream(bo);
            oo.writeObject(session);
            bytes = bo.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bytes;
    }

    /**
     * 字节转session
     *
     * @param bytes 字节流
     * @return Session
     */
    public Session byteToSession(byte[] bytes) {
        ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
        ObjectInputStream in;
        SimpleSession session = null;
        try {
            in = new ObjectInputStream(bi);
            session = (SimpleSession) in.readObject();
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }
        return session;
    }
}
